/**
 * 
 */
package remato.client.model;

import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Map;

import remato.client.controller.Assert;
import remato.client.controller.exceptions.RematoClientException;
import remato.client.model.facade.IModelPersistable;
import remato.client.model.facade.INodeChild;
import remato.client.model.facade.INodeParent;
import remato.client.model.facade.ModelSystem;
import remato.domain.common.RequirementSystem;

import com.wgo.bpot.common.removeme.Persistent;

/**
 * Makes sure that that there is only one wrapper instance for each domain model
 * concept.
 * 
 * @author petterei
 * @version $Id: WrapperFactory.java,v 1.4 2006-02-01 12:49:23 petterei Exp $
 */
public class WrapperFactory {
	
	public static final String WRAPPER_SUFFIX = "Wrapper";
	public static final Package CLIENT_MODEL_PACKAGE = Package.getPackage("remato.client.model");
	
	private  Map<Class<? extends IModelPersistable>, ModelSystem<?>> systems = new Hashtable<Class<? extends IModelPersistable>, ModelSystem<?>>();
	private Map<Persistent, IModelPersistable> wrappers = new Hashtable<Persistent, IModelPersistable>();

	public WrapperFactory(RequirementSystem system) {
		systems.put(ProjectWrapper.class, new RootProjectSystemWrapper(system));
		systems.put(StakeholderWrapper.class, new StakeholderSystemWrapper(system));
		systems.put(PropertySetWrapper.class, new PropertyDefinitionSetSystemWrapper(system));
	}
	
	@SuppressWarnings("unchecked")
	public <T extends INodeChild> ModelSystem<T> getSystem(Class<T> type) {
//		RequirementPlugin.log(new ModelStatus(IStatus.INFO, getClass().getSimpleName() + ": retrieving system for: " + type.getSimpleName() ));
		ModelSystem<T> system = (ModelSystem<T>)systems.get(type);
//		RequirementPlugin.log(new ModelStatus(IStatus.INFO, getClass().getSimpleName() + ": returning system: " + system ));
		return system;
	}
	
	public Collection<ModelSystem<?>> getSystems() {
		return systems.values();
	}
	
	@SuppressWarnings("unchecked")
	public synchronized <T extends IModelPersistable> T getWrapper(Persistent persistent) {
		Assert.warningIfFalse(! (persistent instanceof RequirementSystem), getClass().getSimpleName() + ": tried to get system from wrapper-method." );
		T wrapper = null;
		synchronized (wrappers) {
			wrapper = (T)wrappers.get(persistent);
			if (null == wrapper) {
				wrapper = (T)createWrapper(new Object[]{persistent});
				wrappers.put(persistent, wrapper);
			}
		}
		return wrapper;
	}

	@SuppressWarnings("unchecked")
	public synchronized <T extends IModelPersistable> T getWrapper(Persistent persistent, INodeParent parent) {
		Assert.warningIfFalse(! (persistent instanceof RequirementSystem), getClass().getSimpleName() + ": tried to get system from wrapper-method.");
		T wrapper = null;
		synchronized (wrappers) {
			wrapper = (T)wrappers.get(persistent);
			if (null == wrapper) {
				wrapper = (T)createWrapper(new Object[]{persistent, parent});
				wrappers.put(persistent, wrapper);
			}
		}
		return wrapper;
	}
	
	private static IModelPersistable createWrapper(Object[] constructorArgs){
		Class persistentInterface = constructorArgs[0].getClass().getInterfaces()[0];
		Class[] parameterClasses = new Class[constructorArgs.length];
		parameterClasses[0] = persistentInterface;
		for (int i = 1; i < constructorArgs.length ; i++) {
			parameterClasses[i] = constructorArgs[i].getClass();
		}
		String wrapperClassString = CLIENT_MODEL_PACKAGE.getName() + "." + persistentInterface.getSimpleName() + WRAPPER_SUFFIX;
		IModelPersistable wrapper = null;
		try {
			Class wrapperClass = Class.forName(wrapperClassString);
			Constructor constructor = wrapperClass.getDeclaredConstructor(parameterClasses);
			wrapper = (IModelPersistable) constructor.newInstance(constructorArgs);
		} catch (Exception e) {
			throw new RematoClientException("Could not create "+persistentInterface.getSimpleName()+"Wrapper. Parameters: " +(constructorArgs.toString()), e);
		}
		return wrapper;
	}
}
